#coding: utf8
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db import transaction, connection
from tornado.util import unicode_type
from backend.libs import myjson as json
from hashlib import md5
from pprint import pprint as pp
from backend.models import BMSUser
import tornado.web
import functools
import settings


class Condition(dict):

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        self[name] = value

    def __getitem__(self, name):
        return self.get(name, '')

    def __setitem__(self, name, value):
        self[name] = value


class BaseController(tornado.web.RequestHandler):

    ignore_uris = [
        '/api/signin',
        '/api/login',
    ]

    def pp(self, obj):
        pprint(obj)

    def initialize(self, *args, **kwargs):
        self.has_except = False

    def is_ignored(self, uri):
        for u in self.ignore_uris:
            if uri.find(u) != -1:
                return True
        return False

    def prepare(self):
        if not self.is_ignored(self.request.uri) and not self.current_user:
            raise tornado.web.HTTPError(403)

    @property
    def nav_uri(self):
        return self.request.uri.split('/')[1] or '/'

    @property
    def full_uri(self):
        print('#'*20, self.request.uri or '/')
        return self.request.uri or '/'

    def render_view(self, template_name, **kwargs):
        return self.render(template_name, **kwargs)

    def render_str(self, data=''):
        return self.write(data)

    def render_obj(self, data=None):
        return self.write(data)

    def render_json(self, code=0, msg='', data=None):
        data = data or {}
        json_relt = json.dumps({'code': code, 'msg': msg, 'data': data})
        
        callback = self.get_argument('callback', '', True)
        if callback:
            json_relt = '%s(%s)' % (callback, json_relt)
        return self.finish(json_relt)

    def render_json_code_999(self, msg='', data=None):
        return self.render_json(code=999, msg=msg, data=data)

    def redirect_to(self, url, flash=None):
        return self.redirect(url)

    def get_object_or_404(self, clazz, id, related=False):
        try:
            _id = int(id)
            return clazz.objects.select_related().get(pk=_id) if related \
                    else clazz.objects.get(pk=_id)
        except clazz.DoesNotExist:
            raise tornado.web.HTTPError(404)
        except ValueError: # 表示_id不是整形
            raise tornado.web.HTTPError(404)

    def get_object_or_none(self, clazz, id, related=False):
        try:
            return self.get_object_or_404(clazz, id, related)
        except tornado.web.HTTPError:
            return None

    def get_current_user(self):
        token = self.request.headers.get('Authorization', '')
        if not token:
            return None
        _id = token.split('|')[-1]
        op = BMSUser.objects.filter(id=_id).first()
        if not op:
            return None

        s = '%s|%s|huanxicm' % (op.id, op.username)
        new_token = md5(s.encode('utf8')).hexdigest() + '|%s' % op.id
        if new_token != token:
            return None
        return op


    def pagination(self, object_list, number_per_page=50, page_num=-1, m2m_fields=[], o2o_fields=[]):

        if page_num <= 0: page_num = self.int_argument('pageNum', 0)
        if page_num <= 0: page_num = 1
        paginator = Paginator(object_list, number_per_page)
        try:
            pagination_objects = paginator.page(page_num)
        except PageNotAnInteger:
            pagination_objects = paginator.page(1) # If page is not an integer, deliver first page.
        except EmptyPage:
            pagination_objects = paginator.page(paginator.num_pages) # If page is out of range (e.g. 9999), deliver last page of results.
        records_total = pagination_objects.paginator.count

        return dict(
            pageNum = pagination_objects.number,
            pageSize = number_per_page,
            pageTotal = pagination_objects.paginator.num_pages,
            recordsTotal = records_total,

            page_num = pagination_objects.number,
            page_size = number_per_page,
            page_total = pagination_objects.paginator.num_pages,
            records_total = records_total,
            records = [obj.to_dict(m2m_fields=m2m_fields, o2o_fields=o2o_fields) for obj in pagination_objects.object_list]
        )

    def pagination_contain_objects_no_records(self, object_list, number_per_page=50, page_num=-1, m2m_fields=[], o2o_fields=[]):

        if page_num <= 0: page_num = self.int_argument('pageNum', 0)
        if page_num <= 0: page_num = 1
        paginator = Paginator(object_list, number_per_page)
        try:
            pagination_objects = paginator.page(page_num)
        except PageNotAnInteger:
            pagination_objects = paginator.page(1) # If page is not an integer, deliver first page.
        except EmptyPage:
            pagination_objects = paginator.page(paginator.num_pages) # If page is out of range (e.g. 9999), deliver last page of results.
        records_total = pagination_objects.paginator.count

        return dict(
            pageNum = pagination_objects.number,
            pageSize = number_per_page,
            pageTotal = pagination_objects.paginator.num_pages,
            recordsTotal = records_total,

            page_num = pagination_objects.number,
            page_size = number_per_page,
            page_total = pagination_objects.paginator.num_pages,
            records_total = records_total,
            objects = pagination_objects.object_list
        )

    def pagination_contain_objects(self, object_list, number_per_page=50, page_num=-1, m2m_fields=[]):

        if page_num <= 0: page_num = self.int_argument('pageNum', 0)
        if page_num <= 0: page_num = 1
        paginator = Paginator(object_list, number_per_page)
        try:
            pagination_objects = paginator.page(page_num)
        except PageNotAnInteger:
            pagination_objects = paginator.page(1) # If page is not an integer, deliver first page.
        except EmptyPage:
            pagination_objects = paginator.page(paginator.num_pages) # If page is out of range (e.g. 9999), deliver last page of results.
        records_total = pagination_objects.paginator.count

        return dict(
            pageNum = pagination_objects.number,
            pageSize = number_per_page,
            pageTotal = pagination_objects.paginator.num_pages,
            recordsTotal = records_total,

            page_num = pagination_objects.number,
            page_size = number_per_page,
            page_total = pagination_objects.paginator.num_pages,
            records_total = records_total,
            records = [ obj.to_dict(m2m_fields) for obj in pagination_objects.object_list ],
            objects = pagination_objects.object_list
        )
    

    def int_argument(self, k, default=0):
        v = self.get_argument(k, default)
        try:
            return int(v)
        except:
            return default

    def write_error(self, status_code, *args, **kwargs):
        if settings.DEBUG:
            # from web.py
            # in debug mode, try to send a traceback
            return super(BaseController, self).write_error(status_code, *args, **kwargs)
        else:
            # print '?'*20, status_code
            if status_code == 500:
                return self.write('sorry, interval server error 500')
            if status_code == 404:
                return self.write('sorry, resource not found!')
            if status_code == 403:
                return self.write('sorry, request forbidden!')
            return self.write('sorry, unknow error!')


class PageNotFoundController(BaseController):

    def get(self):
        raise tornado.web.HTTPError(404)

